"""
/*
 * Copyright 2011 OpenWAF.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
 """
from HTMLParser import WAFHTMLParser
from HTMLParser import HTMLViewNode
from HTMLParser import HTMLView 
from ConfigReader import WAFConfig
from Analyzer import SHelper, SGlobal, SClass
def markRefsForControl(view_file,cls):
    hv = WAFHTMLParser(view_file, True)
    cons=cls.getConstructor([], None)
    if cons==None:
        raise Exception("No default constructor found for "+cls.fullname +" in "+view_file) 
    markRefsForView(hv.rootNode,cls,cons)
def processControl(view_file,cls):
    hv = WAFHTMLParser(view_file, True)
    ids = {}
    id_list=[0]
    id_list.append({})
    getIds(hv.rootNode,ids,id_list)
    code=[]
    correctViewForCommonErrors(hv.rootNode)
    getHTMLViewCodeWithoutID(hv.rootNode, code)
    fcode=[]
    line="".join(code)
    line=line.replace("\"","\\\"")
    fcode.append("var c=null")
    fcode.append("var m=null")
    #dure to some error in IE we need seperate div for each element creation  in other browser single global div is sufficient
    fcode.append(WAFConfig.getWAFRootObjectName()+"._cb.d=document.createElement('div');");
    fcode.append(WAFConfig.getWAFRootObjectName()+"._cb.d.innerHTML=\""+line+"\";");        
    fcode.append("this.elm="+WAFConfig.getWAFRootObjectName()+"._cb.d.firstChild;");
    fcode.append("this.elm._wo_=this;");       
    code=getJavaScriptForView(hv.rootNode, [],cls,id_list)
    fcode=fcode+code
    return fcode
def containsId(a):
    for i in a:
        if i[0]=="id" or i[0]=="ID": return True
    return False
def containsIdGet(a):
    for i in a:
        if i[0]=="id" or i[0]=="ID":  
            n=i[1]
            if n==None:
                raise Exception("id is none")
            return removeQuote(n)
    print a
    raise Exception("id not found")
def removeQuote(a):
    if a.startswith("\""):
        return a[1:-1]
    return a        

def getIds(rootNode, ids,id_count):    
    if rootNode.nodeType in [HTMLViewNode.NODE_COMMENT,HTMLViewNode.NODE_DECL]:return
    for ch in rootNode.childNodes:
        getIds(ch, ids,id_count) 
    if containsId(rootNode.attributes):        
        value = containsIdGet(rootNode.attributes)
        if ids.has_key(value):
            raise Exception("Exception duplicate id " + value)
        ids[value] = rootNode
        id_count[1][rootNode]="this."+value
        rootNode.id=value
    elif rootNode.name.startswith("WAF:"):
        i="__wid_"+str(id_count[0])
        id_count[0]+=1
        rootNode.attributes.append(["id",i])
        id_count[1][rootNode]=i
        ids[i] = rootNode
        rootNode.id=i
    
def correctViewForCommonErrors(rootNode):
    """
    1 table must contain table body if not present it will be added automatically
    """
    if rootNode.nodeType in [HTMLViewNode.NODE_COMMENT,HTMLViewNode.NODE_DECL]:return 
    if rootNode.name.lower()=="table":
        last_section=None
        l=len(rootNode.childNodes)
        nnodes=[]
        for i in range(l):
            ch=rootNode.childNodes[i]
            if ch.name.lower()=="tr":
                if last_section==None:
                    last_section=HTMLViewNode()
                    last_section.name="tbody"
                    nnodes.append(last_section)
                last_section.childNodes.append(ch)
                correctViewForCommonErrors(ch)
            else:
                last_section=None
                nnodes.append(ch)
                correctViewForCommonErrors(ch)           
        rootNode.childNodes=nnodes
        return 
    for ch in rootNode.childNodes:
        correctViewForCommonErrors(ch)
                    
        
def getHTMLViewCodeWithoutID(rnode, code):
    if rnode.nodeType in [HTMLViewNode.NODE_COMMENT,HTMLViewNode.NODE_DECL]:return
    if rnode.nodeType==HTMLViewNode.NODE_DATA:
        if len(rnode.innerHTML.strip()) != 0:
            code.append(rnode.innerHTML.strip()+" ")                
        return
    if len(rnode.name.strip()) != 0:
        if rnode.name.lower()=="style":return
        if rnode.name.startswith("WAF:"):
            s ="<div"
        else:
            s = "<" + rnode.name
        if not rnode.name.startswith("WAF:"):
            for atr in rnode.attributes:
                name=atr[0]
                value=atr[1]
                if name == "id" or name == "ID":continue
                s += " " + name
                if value != None:
                    s += "=\"" + removeQuote(value) + "\""

        if len(rnode.childNodes) > 0:
            s += ">"
            code.append(s)
        else:
            if rnode.name.startswith("WAF:"):
                s+="></div>"
            elif rnode.name.lower() in ["div" ,"script","span","ol","ul","textarea","iframe","table" ]:
                s += "></"+rnode.name +">" #need to investigate might be some bug in browser in rendering this
            else:
                s += "/>"
            code.append(s)
            return

    if (len(rnode.name.strip()) != 0):
        if rnode.name.startswith("WAF:"):
            for cnode in rnode.childNodes:
                if cnode.nodeType!=HTMLViewNode.NODE_ELEMENT:continue
                code.append("<div>")
                for cc in cnode.childNodes:
                    getHTMLViewCodeWithoutID(cc, code)
                code.append("</div>")
            code.append("</div>")
            return 
    for cnode in rnode.childNodes:
        getHTMLViewCodeWithoutID(cnode, code)
    if len(rnode.name.strip()) != 0:
        code.append("</" + rnode.name + ">")
                
def addMethodRef(method,mid):
    if id not in method.refs:
        method.refs.append(mid)
    if not SGlobal.method_refs.has_key(method.mid):
        SGlobal.method_refs[method.mid]=method
def markRefsForView(node,cls,cur_method):
    count=0
    for cnode in node.childNodes:
        markRefsForView(cnode,cls,cur_method)
        count+=1
    if node.name.startswith("WAF:"):
        cname=node.name[4:]
        control_cls=SHelper.getClassOnNameFromImported(cname, cls)
        if control_cls==None:
            raise Exception("Class not found with name ",cname)
        cons=control_cls.getConstructor([],None)
        if cons==None:
            raise Exception("Class dont have default constructor "+control_cls.fullname)
        addMethodRef(cur_method, cons.mid)
        for atr in node.attributes:
            name=atr[0]
            if name.lower()=="id":continue
            name="set"+name.capitalize()
            m=control_cls.getMethodWithThisAccess(name,[SGlobal.stringclass.mytype],False, None,cls)
            if m!=None:
                addMethodRef(cur_method, m.method.mid)
        for cn in node.childNodes:
            if cn.nodeType!=HTMLViewNode.NODE_ELEMENT:continue            
            name=cn.name
            #count=countNonEmptyChilds(cn)
            types=getMethodArgsTypes(cn.childNodes, cls)
            #t=SHelper.getClassOnFullName("com.openwaf.client.dom.Element").mytype
            #types=[]
            #for i in range(count):
            #    types.append(t)
            m=control_cls.getMethodWithThisAccess(name,types,False,None,cls)
            if m==None:
                raise Exception(cls.fullname+"(View Error)Method not found "+name+" for class "+control_cls.fullname)
            addMethodRef(cur_method, m.method.mid)
def getJavaScriptForView(node,arr,cls,id_list):
    count=0
    code=[]
    for cnode in node.childNodes:
        arr.append(int(count))
        code+=getJavaScriptForView(cnode,arr,cls,id_list)
        arr.pop()
        count+=1
    if node.id==None:return code
    if node.name.startswith("WAF:"):
        value = containsIdGet(node.attributes)
        cname=node.name[4:]
        control_cls=SHelper.getClassOnNameFromImported(cname, cls)
        if control_cls==None:
            raise Exception("Class not found with name ",cname)
        cons=control_cls.getConstructor([],None)
        if cons==None:
            raise Exception("Class dont have default constructor "+control_cls.fullname)
        line = "(new " + control_cls.getPrototype() + "())." + cons.getJSName() + "()"
        varname=value;
        if value.startswith("__wid_"):
            code.append("var "+value+"="+line+";")
        else:
            field=cls.getFieldWithThisAccess(value, False,cls)
            if field==None:
                raise Exception("Field is not declared in class "+cls.fullname+" "+value)
            field=field.field            
            code.append("this."+field.getJSName()+"="+line+";")
            varname="this."+field.getJSName()
        for a in node.attributes:
            atr=a[0]
            
            if atr.lower()=="id":continue
            atr_value=removeQuote(a[1])
            name="set"+atr[0:1].upper()+atr[1:]
            m=control_cls.getMethodWithThisAccess(name,[SGlobal.stringclass.mytype],False, None,cls)
            if m==None:
                code.append( WAFConfig.getWAFRootObjectName()+"._cb.sa("+varname+".elm,\""+atr+"\",\""+atr_value+"\");")
            else:
                code.append(varname+"."+m.method.getJSName()+"(\""+atr_value+"\");")
              
        s="c="+WAFConfig.getWAFRootObjectName() + "._cb.g(this.elm,["
        s+=",".join([str(n) for n in arr[1:]])
        s+="]);"
        code.append(s)  
        mcode=[]
        mcode.append("m="+WAFConfig.getWAFRootObjectName()+"._cb.ge(c);")
        mcount=0
        for cn in node.childNodes:
            if cn.nodeType!=HTMLViewNode.NODE_ELEMENT:continue            
            name=cn.name
            childs=getNonEmptyChilds(cn)
            count=len(childs)
            t=SHelper.getClassOnFullName("com.openwaf.client.dom.Element").mytype
            types=getMethodArgsTypes(cn.childNodes, cls)
            #for i in range(count):
            #    types.append(t)
            m=control_cls.getMethodWithThisAccess(name,types,False,None,cls)
            if m==None:
                raise Exception(cls.fullname+"(View Error)Method not found "+name+" for class "+control_cls.fullname)
            mcode.append("var a="+WAFConfig.getWAFRootObjectName()+"._cb.ge(m["+str(mcount)+"]);")
            line=varname+"."+m.method.getJSName()+"("
            pars=[]
            for i in range(count):
                if types[i]==t:
                    pars.append("a["+str(i)+"]")
                else:
                    if id_list[1].has_key(childs[i]):
                        if containsId(childs[i].attributes):
                            fname=containsIdGet(childs[i].attributes)                            
                            if not fname.startswith("__wid"):                                                    
                                field=cls.getField(fname)
                                pars.append("this."+field.getJSName())
                            else:
                                pars.append(fname)
                        else:                    
                            pars.append(id_list[1][childs[i]])
                    else:
                        raise Exception("UnExpected Some went wrong please report this.")
            line=line+",".join(pars)+");"
            mcode.append(line)
            mcount+=1
        if len(mcode)>1:
            code.append(mcode)
        code.append(WAFConfig.getWAFRootObjectName()+"._cb.rn(c,"+varname+".elm);")
    else:        
        field=cls.getFieldWithThisAccess(node.id, False,cls)
        if field==None:
            raise Exception("Field is not declared in class "+cls.fullname+" "+node.id)
        field=field.field     
        s="this."+field.getJSName()+"="+WAFConfig.getWAFRootObjectName() + "._cb.g(this.elm,["
        s+=",".join([str(n) for n in arr[1:]])
        s+="]);"
        code.append(s)      
    return code
def countNonEmptyChilds(rnode):
    count=0
    for ch in rnode.childNodes:
        if ch.nodeType==HTMLViewNode.NODE_ELEMENT:
            count+=1
        elif ch.nodeType==HTMLViewNode.NODE_DATA:
            if ch.innerHTML!=None and len(ch.innerHTML.strip())>0:
                count+=1
    return count
def getNonEmptyChilds(rnode):
    cs=[]
    for ch in rnode.childNodes:
        if ch.nodeType==HTMLViewNode.NODE_ELEMENT:
            cs.append(ch)
        elif ch.nodeType==HTMLViewNode.NODE_DATA:
            if ch.innerHTML!=None and len(ch.innerHTML.strip())>0:
                cs.append(ch)
    return cs
def getMethodArgsTypes(nodes,cls):
    if nodes==None:return []
    types=[]
    t=SHelper.getClassOnFullName("com.openwaf.client.dom.Element").mytype
    for cn in nodes:
        if cn.nodeType!=HTMLViewNode.NODE_ELEMENT:continue
        if cn.name.startswith("WAF:"):
            name=cn.name[4:]
            a=SHelper.getClassOnNameFromImported(name, cls)
            if a==None:
                raise Exception("Class not found in view "+name)
            types.append(a.mytype)            
        else:
            types.append(t)
    return types

